#ifndef LL1PARSER_H
#define LL1PARSER_H

#include <vector>
#include <iostream>
#include <string>
#include <map>
#include <stack>
#include "rules.h"
#include "token.h"
#include <algorithm>
#include "tree.h"
#include "rules_utilities.h"
#include "types.h"

/* Синтаксичний аналізатор для LL(1)-граматик */

/* Лямда правило ^ - завжди розміщається на початку правил у правій частині для всіх NT-символів; це зпрощує пошук пустих правил */

class Parser {
    std::vector<Rules> first_rules, follow_rules; // граматики для множин first и follow
    std::map<std::string, std::vector<std::string> > first, follow; // множини first и follow - потрібні за алгоритмом
    TreeOutput *output_tree; // поточне дерево виводу. Використовується для заповнення mas_trees
    std::vector<TreeOutput*> output_trees; // дерева виводу кожної окремої ф-ції
    int __sysParsing(const std::vector<TokenEntry>&, std::string, int&); // МП-автомат(підбір альтернатив)
    int __sysParsingCompletion(std::string); // кінцева стадія роботи ф-ції __sysParsing
    void __fillFirstSet(); // заповнення множини first
    void __fillFollowSet(); // заповнення множини follow
    // ф-ція заповнення таблиці ідентифікаторів на синтаксичному дереві
    int __fillIdentificatorTable(std::map<std::string, IdentificatorEntry>&, TreeOutput*);
public:
    Parser(const std::vector<Rules>&, const std::vector<Rules>&);
    ~Parser()
    {
        for (int i = 0; i < output_trees.size(); i++) delete output_trees[i];
    }
    int parsing(const std::vector<TokenEntry> &tokens, std::map<std::string, std::map<std::string, IdentificatorEntry> > &ident_table)
    {
        int token_pos = 0;
        // заносимо кількість параметрів в таблицю ідентифікаторів стандартної ф-ції stdout
        ident_table["stdout"][".s_s"] = IdentificatorEntry(".s_s", String, 4);
        if (tokens[0].token_type == Function) // синтаксичний аналіз опису ф-цій
        {
            while (true) {
                if (__sysParsing(tokens, "FUNCDECLARATION", token_pos) == 1) return 1; // помилка
                output_trees.push_back(output_tree);
                __fillIdentificatorTable(ident_table[output_tree->getChildNodes()[0]->getNodeName()], output_tree);
                output_tree = 0;
                if (token_pos == tokens.size()) break;
            }
        }
        else return 1; // помилка
        return 0;
    }
    const std::vector<TreeOutput*>& getOutputTrees() const { return output_trees; }
};

#endif // LL1PARSER_H
